﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed
#pragma once

#include "GcmpModelInterface.h"
#include "INoElementIdDbObject.h"
#include "RegenDataId.h"

namespace gcmp
{
    class ElementId;
    class ICalculatorCollection;
    class ICopyContext;
    class IDocument;
    class ICopyStrategyReporter;
    struct SecondaryAttachingJoinData;

    /// \brief 多构件连接行为组件
    /// 
    /// 通过该组件来定制化一些多构件连接的行为，如拷贝/删除时的行为，或决定多构建连接时的计算顺序/依赖关系
    /// 但是具体的连接计算请通过IElementJoinBehavior来实现
    class GCMP_MODEL_INTERFACE_EXPORT IMultiJoinDataComponent : public INoElementIdDbObject
    {
        DEFINE_CAST_DERIVED(gcmp::IMultiJoinDataComponent, gcmp::INoElementIdDbObject);
    public:
        /// \brief 析构函数
        virtual ~IMultiJoinDataComponent() {};

#pragma region 关联更新

        /// \brief 在添加次对象连接数据后进行关联更新数据标记
        /// \param secondaryAttachingJoindata 次对象连接数据
        /// \return void
        virtual void MarkRegenDataAfterAddSecondaryAttachingJoinData(const SecondaryAttachingJoinData& secondaryAttachingJoindata) const = 0;

        /// \brief 在删除次对象连接数据后进行关联更新数据标记
        /// \param secondaryAttachingJoindata 次对象连接数据
        /// \param index 被删除对象原有的索引
        /// \return void
        virtual void MarkRegenDataAfterDeleteSecondaryAttachingJoinData(const SecondaryAttachingJoinData& secondaryAttachingJoindata, size_t index) const = 0;

        /// \brief 报告连接形体计算器需额外关注的数据
        ///
        /// 会影响连接计算器的数据需要在此函数中报告出来
        /// \param regenDataIds 计算输出数据所要用到数据的RegenDataId，regenDataIds既是输入数据，又是输出数据，不允许在函数内清空数据。
        /// \param currentAttachingElementId 当前正在收集输入数据的次对象的ElementId
        /// \return void
        virtual void ReportExtraInputDataIds(std::vector<RegenDataId> & regenDataIds, const ElementId& currentAttachingElementId) const = 0;

        /// \brief 用于扩展宿主ElementJoin的计算器（如添加计算器，用于传播数据的更改，可以结合上面的ReportExtraInputDataIds来决定次连接对象的计算顺序）
        /// 
        /// \param calculators 计算器收集器
        /// \return void
        virtual void GetCalculators(ICalculatorCollection* calculators) const = 0;
#pragma endregion 关联更新

#pragma region 消隐

        /// \brief 获取需要执行消隐的目标构件的ElementIds
        /// \param targetIds 需要执行消隐的目标构件的ElementIds
        /// \param currentElementId 正在处理消隐的构件ElementId
        /// \return void
        virtual void GetElementIdsToHideEdge(std::set<ElementId>& targetIds,const ElementId currentElementId) const = 0;

#pragma endregion 消隐

#pragma region 复制粘贴
        /// \brief 报告引用的Element的id为拷贝、保持、放弃
        /// \param copyContext 拷贝上下文
        /// \param reporter 拷贝策略报告器
        /// \return void
        /// \see ICopyStrategyReporter
        virtual void Report(const ICopyContext & copyContext, ICopyStrategyReporter& reporter) const = 0;

        /// \brief 复制粘贴后更新操作
        ///
        /// 复制粘贴后需要的后处理可以放在这里，比如：对被复制粘贴后的元素进行重新命名
        ///
        /// \return void
        virtual void UpdateAfterCopy(const ICopyContext& copyContext) = 0;

        /// \brief 当Weak Parent被拷贝但此Element没有同时被拷贝时，此函数被调用
        ///
        /// 调用时机：当Weak Parent被拷贝但此Element没有同时被拷贝时，此Element会通过此接口收到通知。
        /// 用途：一般用来处理Element存在Id容器的情况。
        /// 例子：当Sketch里的对象被拷贝但Sketch本身没被拷贝时，Sketch通过实现此接口将拷贝的新对象加到SketchObjectId容器中。
        ///
        /// \param pDocumentFrom 源文档
        /// \param pSource 被拷贝的IElement指针
        /// \param pCopy 拷贝得到的IElement指针
        /// \return void
        virtual void UpdateForWeakParentCopied(
            const IDocument* pDocumentFrom,
            const IElement* pSource,
            const IElement* pCopy
        ) const = 0;

        /// \brief 当弱依赖的Element被删除后，此接口会被调用。
        /// \param deleteElementIds 表示将被删除的Element ids
        /// \param isMainJoinIdsChanged 表明删除后主连接中的Id是否发生了改变，如主连接中的某个对象被删除，可能会从次连接对象中找一个进行补位。如果为true的话，会通过关联更新机制使主连接对象的连接图形表达进行重新计算。
        /// \return void
        virtual void UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds, bool &isMainJoinIdsChanged) = 0;

        /// \brief 决定是否需要一起被拷贝
        ///
        /// 调用时机：当一个对象是否拷贝不仅仅取决于一个对象时，无法通过Report来直接决定是否拷贝。
        /// 用途：根据待拷贝对象集来决定自己是否拷贝
        /// 
        /// 需注意，这里IMultiJoinDataComponent要处理决定整个ElementJoin是否拷贝
        /// 
        /// \param copyContext 拷贝上下文
        /// \return 返回true表示需要，false表示不需要。
        virtual bool ShouldBeCopiedTogether(const ICopyContext & copyContext) const = 0;

        /// \brief 判断是否可以复制
        /// \param pErrorMessage 输出信息
        /// \return 返回true表示可以复制，false表示不能复制。
        virtual bool CanBeCopied(std::wstring* pErrorMessage) const = 0;

#pragma endregion 复制粘贴
    };
}


